/*我的K线我做主,我想咋画就咋画*/
import {addToDefaultGroupByStockCode, deleteFromDefaultGroupByStockCode} from '@/api/eye/myAttention'

// 创建一个构造函数
function DragonKLine(option) {
  this.option = option;
}

/**
 *
 * @param divId DIV的ID
 * @param title 标题
 * @param dateData 日期数组
 * @param kLineData 价格数组
 */
export function initDragonKLine(divId, title, dateData, kLineData) {
  const container = document.getElementById(divId)
  exeOneTime(container)
  let chart = new DragonKLine('To Do');
  chart.drawByCanvas(container, title, dateData, kLineData);
}

let executed = false;

/**
 * 执行一次函数,创建十字线
 */
function exeOneTime() {
  if (!executed) {
    console.log("执行一次");
    // 创建一个新的div元素
    let divX = document.createElement("div");
    divX.id = 'crossLineX';
    divX.style.cssText = "display: none;border: 1px dashed #000; position: fixed; pointer-events: none; z-index: 1;";
    let divY = document.createElement("div");
    divY.id = 'crossLineY';
    divY.style.cssText = "display: none; border: 1px dashed #000;position: absolute; pointer-events: none; z-index: 1;";
    let divPrice = document.createElement("div");
    divPrice.id = 'crossLinePrice';
    divPrice.style.cssText = "display: none; position: fixed; pointer-events: none; z-index: 1; padding: 4px; border: 1px solid #5fa0c4; background-color: #c4895f; font-size: 14px; font-weight: bold;";

    // 将div添加到body元素中
    document.body.appendChild(divX);
    document.body.appendChild(divY);
    document.body.appendChild(divPrice);
  }
  executed = true;
}

DragonKLine.prototype.drawByCanvas = function (container, title, dateData, kLineData) {
  console.log("调用绘制方法,", container.id);
  let chartId = container.id
  let titleFontSize = '14px'

  // 创建一个所有标题的顶级div元素
  let allTitleDiv = document.createElement("div");
  allTitleDiv.id = chartId + "allTitleDiv";
  allTitleDiv.style.cssText = 'position: relative;';
  // 创建价格标题div
  let stockPriceTitle = document.createElement("div");
  stockPriceTitle.id = chartId + "stockPriceTitle";
  stockPriceTitle.style.cssText = 'position:absolute';
  allTitleDiv.appendChild(stockPriceTitle)
  // 创建stock名称div
  let stockNameTitle = document.createElement("div");
  stockNameTitle.id = chartId + "stockNameTitle";
  stockNameTitle.innerText = title;
  stockNameTitle.style.cssText = 'float:right;cursor:pointer';
  stockNameTitle.onmouseover = function () {
    this.style.color = 'red';
  };
  stockNameTitle.addEventListener('click', function (event) {
    if (event.ctrlKey) { // 检查是否按下了Ctrl键
      addToDefaultGroupByStockCode(title.split('_')[0])
    } else {
      deleteFromDefaultGroupByStockCode(title.split('_')[0])
    }
  });
  allTitleDiv.appendChild(stockNameTitle)
  // 创建成交量标题div
  let volumeTitle = document.createElement("span");
  volumeTitle.id = chartId + "volumeTitle";
  allTitleDiv.appendChild(volumeTitle);
  // 创建kdj标题div
  let kdjTitle = document.createElement("span");
  kdjTitle.id = chartId + "kdjTitle";
  allTitleDiv.appendChild(kdjTitle);
  // 创建macd标题div
  let macdTitle = document.createElement("span");
  macdTitle.id = chartId + "macdTitle";
  allTitleDiv.appendChild(macdTitle);
  // 创建boll标题div
  let bollTitle = document.createElement("span");
  bollTitle.id = chartId + "bollTitle";
  allTitleDiv.appendChild(bollTitle);

  container.appendChild(allTitleDiv);

  // 创建canvas
  let kLineCanvas = document.createElement("canvas");
  kLineCanvas.id = chartId + 'kLineCanvas'
  kLineCanvas.style.borderBottom = 'solid 1px #3F3F3F';
  container.appendChild(kLineCanvas);
  let createVolumeCanvas = document.createElement("canvas");
  createVolumeCanvas.id = chartId + 'volumeCanvas'
  createVolumeCanvas.style.borderBottom = 'solid 1px #3F3F3F';
  container.appendChild(createVolumeCanvas);
  let createKdjCanvas = document.createElement("canvas");
  createKdjCanvas.id = chartId + 'kdjCanvas'
  createKdjCanvas.style.borderBottom = 'solid 1px #3F3F3F';
  container.appendChild(createKdjCanvas);
  let createMacdCanvas = document.createElement("canvas");
  createMacdCanvas.id = chartId + 'macdCanvas'
  createMacdCanvas.style.borderBottom = 'solid 1px #3F3F3F';
  container.appendChild(createMacdCanvas);
  let createBollCanvas = document.createElement("canvas");
  createBollCanvas.id = chartId + 'bollCanvas'
  createBollCanvas.style.borderBottom = 'solid 1px #3F3F3F';
  container.appendChild(createBollCanvas);

  console.log(`当前可用区域宽度：${window.innerWidth}px，当前可用区域高度：${window.innerHeight}px`);

  // 获取数据
  const volumeData = kLineData.map(n => n[4]);                 //成交量
  // console.log('成交量:', volumeData)
  kLineData = kLineData.map(subArr => subArr.slice(0, 4));      //开收高低价格
  // console.log('K线数据:', kLineData)
  let maPriceArray = [];                                        //均线
  maPriceArray.push({ma: calculateMA(5, kLineData), color: '#000000'})
  maPriceArray.push({ma: calculateMA(10, kLineData), color: '#fc6900'})
  maPriceArray.push({ma: calculateMA(20, kLineData), color: '#ff0011'})
  maPriceArray.push({ma: calculateMA(30, kLineData), color: '#31af06'})
  maPriceArray.push({ma: calculateMA(60, kLineData), color: '#0058ff'})
  maPriceArray.push({ma: calculateMA(125, kLineData), color: '#d521d0'})
  maPriceArray.push({ma: calculateMA(250, kLineData), color: '#863b3b'})
  // console.log('均线数据:', maPriceArray)
  const kdjData = calculateKDJ(kLineData);                     //KDJ数据
  // console.log('kdjData:', kdjData)
  const macdData = calculateMACD(kLineData.map(n => n[1]));    //MACD数据
  // console.log('macdData:', macdData)
  const bollData = calculateBOLL(kLineData);    //BOLL数据
  // console.log('bollData:', bollData)

  //定义公共变量
  const commonWidth = window.innerWidth - 300  //设置canvas公共宽度
  const commonHeight = 100                     //设置canvas公共高度
  const indexChartToTop = 10                   //等指标图离canvas顶部距离
  const indexChartToBottom = 5                 //指标图离canvas底部距离

  // 定义k线图canvas
  const canvas = document.getElementById(chartId + "kLineCanvas");
  const canvasWidth = canvas.width = commonWidth  //设置canvas宽度
  const canvasHeight = canvas.height = 250        //设置canvas高度
  console.log('canvas宽度和高度', canvasWidth, canvasHeight)
  const ctx = canvas.getContext('2d');   // 获取canvas绘画上下文

  // 定义成交量canvas
  const volumeCanvas = document.getElementById(chartId + "volumeCanvas");
  const volumeWidth = volumeCanvas.width = commonWidth
  const volumeHeight = volumeCanvas.height = 50
  const volumeCtx = volumeCanvas.getContext('2d'); // 获取成交量canvas上下文

  // 定义kdj canvas
  const kdjCanvas = document.getElementById(chartId + "kdjCanvas");
  const kdjWidth = kdjCanvas.width = commonWidth
  const kdjHeight = kdjCanvas.height = 80
  const kdjCtx = kdjCanvas.getContext('2d'); // 获取成交量canvas上下文

  // 定义macd canvas
  const macdCanvas = document.getElementById(chartId + "macdCanvas");
  const macdWidth = macdCanvas.width = commonWidth
  const macdHeight = macdCanvas.height = 100
  const macdCtx = macdCanvas.getContext('2d'); // 获取成交量canvas上下文

  // 定义boll canvas
  const bollCanvas = document.getElementById(chartId + "bollCanvas");
  const bollWidth = bollCanvas.width = commonWidth
  const bollHeight = bollCanvas.height = 80
  const bollCtx = bollCanvas.getContext('2d'); // 获取成交量canvas上下文

  //定义k线图十字线
  const crossLineXElem = document.getElementById('crossLineX');     //十字线X轴
  const crossLineYElem = document.getElementById('crossLineY');     //十字线Y轴
  const crossLinePrice = document.getElementById('crossLinePrice'); //十字线X轴价格
  crossLineXElem.style.width = canvasWidth + 'px';  //十字线宽度
  crossLineYElem.style.height = canvasHeight + volumeHeight + kdjHeight + macdHeight + bollHeight + 'px';  //十字线高度

  // 添加交互性
  canvas.addEventListener('mousedown', handleMouseDown);
  canvas.addEventListener('mouseup', handleMouseUp);
  canvas.addEventListener('mousemove', handleMouseMove);
  volumeCanvas.addEventListener('mousemove', handleMouseMove);
  kdjCanvas.addEventListener('mousemove', handleMouseMove);
  macdCanvas.addEventListener('mousemove', handleMouseMove);
  bollCanvas.addEventListener('mousemove', handleMouseMove);
  canvas.addEventListener('mouseleave', handleMouseLeave);
  volumeCanvas.addEventListener('mouseleave', handleMouseLeave);
  kdjCanvas.addEventListener('mouseleave', handleMouseLeave);
  macdCanvas.addEventListener('mouseleave', handleMouseLeave);
  bollCanvas.addEventListener('mouseleave', handleMouseLeave);
  canvas.addEventListener('wheel', handleWheelEvent);
  let moveRange = 0; //鼠标拖动距离

  // K线图变量
  const rect = canvas.getBoundingClientRect();
  const canvasToLeft = rect.left + window.scrollX;
  console.log('canvas离左侧距离:', canvasToLeft)
  const canvasToTop = rect.top + window.scrollY;
  console.log('canvas离顶部距离:', canvasToTop)
  const scaleWidth = 50;                         //Y轴价格刻度宽度
  console.log('Y轴价格刻度宽度', scaleWidth)
  const showDateNum = 12;        // 显示日期的个数
  const kChartToBottom = 25;      // K线图距离底部边距
  const kChartToTop = 20;         // K线图距离顶部边距
  let upColor = '#ff3333';           // 上涨颜色
  let downColor = '#09ab61';       // 下跌颜色
  let itemWidth = 0;             // 烛图宽度
  let maxPrice = 100;            // 最高价格
  let minPrice = 0;              // 最低价格
  let priceRatio = 1;            // 计算坐标比例,定义单位价格所占的高度
  let allPriceDataLength = kLineData.length;             //所有数据长度
  let showCandleNum = calculateShowCandleNum(kLineData.length); //展示烛图个数
  let showStartIndex = kLineData.length - showCandleNum; //K线图展示开始下标
  let showEndIndex = kLineData.length;                   //K线图展示结束下标
  console.log('展示区间下标:', showStartIndex, showEndIndex);

  //绘制K线图、成交量、KDJ、MACD、BOLL
  renderAll()

  function handleMouseDown(event) {
    event.preventDefault();
    console.log('鼠标按下')
    canvas.style.cursor = 'grabbing';
  }

  function handleMouseUp(event) {
    event.preventDefault();
    console.log('松开')
    canvas.style.cursor = 'grab';
    moveRange = 0;
  }

  /**
   * 鼠标移动到K线图显示十字线和当前烛图的价格
   * @param event
   */
  function handleMouseMove(event) {
    event.preventDefault();
    // 通过当前的x坐标值去求得对应实体的下标
    let index = (event.clientX - canvasToLeft - scaleWidth) / itemWidth
    if (index > 0) {
      index = Math.ceil(index)
      if (index % 2 === 1) {
        index = Math.floor(index / 2)
        let range = ((kLineData[index + showStartIndex][1] - kLineData[index + showStartIndex - 1][1]) / kLineData[index + showStartIndex - 1][1] * 100).toFixed(2);
        // 鼠标移动显示价格
        document.getElementById(chartId + 'stockPriceTitle').innerHTML =
          '日期 ' + '<span style="margin-right: 10px; color: red;">' + dateData[index + showStartIndex] + '</span>'
          + '开 ' + '<span style="display: inline-block;width:40px">' + kLineData[index + showStartIndex][0] + '</span>'
          + '收 ' + '<span style="color:red;display: inline-block;width:40px">' + kLineData[index + showStartIndex][1] + '</span>'
          + '高 ' + '<span style="display: inline-block;width:40px">' + kLineData[index + showStartIndex][2] + '</span>'
          + '低 ' + '<span style="display: inline-block;width:40px">' + kLineData[index + showStartIndex][3] + '</span>'
          + '幅度 ' + '<span style="color: red;">' + range + '</span>';

        // 鼠标移动显示成交量
        document.getElementById(chartId + 'volumeTitle').innerHTML = 'VOLUME: <span style="color: blue;">' + volumeData[index + showStartIndex] + '</span>';
        document.getElementById(chartId + 'volumeTitle').style.cssText = 'font-size:' + titleFontSize + ';position: absolute;left: 0;top:' + (canvasHeight + 20) + 'px;';

        // 鼠标移动显示KDJ的值
        document.getElementById(chartId + 'kdjTitle').innerHTML = 'KDJ(9,3,3)'
          + ' K: ' + kdjData.k[index + showStartIndex]
          + ' D: ' + kdjData.d[index + showStartIndex]
          + ' J: ' + '<span style="color: blue;">' + kdjData.j[index + showStartIndex] + '</span>';
        document.getElementById(chartId + 'kdjTitle').style.cssText = 'font-size:' + titleFontSize + ';position: absolute;left: 0;top:' + (canvasHeight + volumeHeight + 25) + 'px;';

        // 鼠标移动显示MACD的值
        document.getElementById(chartId + 'macdTitle').innerHTML = 'MACD(12,26,9)'
          + ' DIF:' + macdData.dif[index + showStartIndex]
          + ' DEA:' + macdData.dea[index + showStartIndex]
          + ' MACD:' + macdData.macd[index + showStartIndex];
        document.getElementById(chartId + 'macdTitle').style.cssText = 'font-size:' + titleFontSize + ';position: absolute;left: 0;top:' + (canvasHeight + volumeHeight + kdjHeight + 30) + 'px;';

      }

      // 计算十字线Y轴位置
      crossLineYElem.style.display = 'block'
      crossLineYElem.style.left = `${event.clientX}px`;
      crossLineYElem.style.top = `${canvasToTop}px`;
    }

    // 计算十字线X轴位置
    crossLineXElem.style.top = `${event.clientY}px`;
    crossLineXElem.style.left = `${rect.left - window.scrollX}px`;
    crossLineXElem.style.display = 'block';

    // 计算十字线price值和位置
    let heightToPrice = (maxPrice - minPrice) / (canvasHeight - kChartToTop - kChartToBottom)// 单位高度对应的价格
    crossLinePrice.style.display = 'block';
    let max = heightToPrice * (canvasHeight - kChartToBottom) + minPrice
    let showPrice = max - (event.clientY - canvas.getBoundingClientRect().top) * heightToPrice
    crossLinePrice.innerText = showPrice.toFixed(2);
    crossLinePrice.style.top = `${event.clientY - 15}px`;
    crossLinePrice.style.left = `${rect.left - window.scrollX}px`;

    // 按下鼠标并拖动
    if (event.buttons === 1) {
      console.log('拖动前下标:', showStartIndex, showEndIndex)
      console.log('拖动:', event.movementX)
      moveRange += event.movementX;
      if (moveRange > 0) {
        console.log('向右拖动累积:', moveRange)
        if (Math.floor(moveRange / itemWidth) >= 1) {
          showStartIndex = showStartIndex - Math.floor(moveRange / itemWidth);
          showEndIndex = showEndIndex - Math.floor(moveRange / itemWidth);
          if (showStartIndex <= 0) {
            console.log('不能再向右拖动了')
            showStartIndex = 0;
            showEndIndex = showCandleNum
          }
          moveRange = 0;
        }

      } else {
        console.log('向左拖动累积:', moveRange)
        if (Math.floor(-moveRange / itemWidth) >= 1) {
          showStartIndex = showStartIndex + Math.floor(-moveRange / itemWidth);
          showEndIndex = showEndIndex + Math.floor(-moveRange / itemWidth);
          if (showEndIndex >= allPriceDataLength) {
            console.log('不能再向左拖动了')
            showEndIndex = allPriceDataLength
            showStartIndex = kLineData.length - showCandleNum
          }
          moveRange = 0;
        }
      }

      console.log('拖动后下标:', showStartIndex, showEndIndex)
      console.log('............重新渲染............')
      renderAll()
    }
  }

  function handleMouseLeave(event) {
    event.preventDefault();
    crossLineXElem.style.display = 'none';
    crossLineYElem.style.display = 'none';
    crossLinePrice.style.display = 'none';
  }

  function handleWheelEvent(event) {
    event.preventDefault();
    let deltaY = event.deltaY; // 鼠标滚动的位移量
    console.log('滚轮缩放：', deltaY)
    console.log('滚轮缩放前下标:', showStartIndex, showEndIndex)
    if (deltaY > 0) {
      console.log('向下滚动:', deltaY)
      showStartIndex = showStartIndex - Math.floor(kLineData.length / 30);
      if (showStartIndex <= 0) {
        console.log('不能再向下滚动')
        showStartIndex = 0;
      }
    } else {
      console.log('向上滚动:', deltaY)
      let after = showStartIndex + Math.floor(kLineData.length / 30);
      if (after >= kLineData.length) {
        console.log('不能再向上滚动')
        return;
      } else {
        showStartIndex = after
      }
    }

    showEndIndex = kLineData.length
    console.log('滚轮缩放后下标:', showStartIndex, showEndIndex)
    console.log('............重新渲染............')
    renderAll()
  }

  /**
   * 绘制K线图
   */
  function drawKLine(dayPriceArray, maPriceArray) {
    ctx.clearRect(0, 0, canvasWidth, canvasHeight)

    maxPrice = Math.max(...dayPriceArray.flat(1));
    minPrice = Math.min(...dayPriceArray.flat(1));
    priceRatio = (canvasHeight - kChartToTop - kChartToBottom) / (maxPrice - minPrice); // 计算坐标比例,定义单位价格所占的高度
    console.log('最低价和最高价,价格转坐标比例', minPrice, maxPrice, priceRatio)

    itemWidth = (canvasWidth - scaleWidth) / (dayPriceArray.length * 2 - 1);
    console.log('烛图水平宽度:', itemWidth)

    // 绘制Y轴价格刻度
    ctx.save();   //保存初始状态，原点(0,0)
    ctx.translate(0, canvasHeight - kChartToBottom) // 重新映射画布上的 (0,0) 位置为新的位置(x,y)
    let divideNum = 4 //等分个数
    const priceStep = Math.round((maxPrice - minPrice) / divideNum);
    console.log('Y轴价格N等分:', priceStep)
    divideNum++;
    console.log('K线图等分个数:', divideNum)

    for (let i = 0; i <= divideNum; i++) {
      // 绘制Y轴价格刻度价格
      let y = i * priceStep * priceRatio;
      ctx.font = '12px Consolas';
      ctx.textAlign = 'right';
      ctx.fillText(String((minPrice + priceStep * i).toFixed(2)), scaleWidth - 5, -y);
    }
    ctx.restore() //重置原点(0,0)

    // 绘制Y轴价格刻度线
    ctx.save();   //保存初始状态，原点(0,0)
    ctx.translate(0, canvasHeight - kChartToBottom) // 重新映射画布上的 (0,0) 位置为新的位置(x,y)
    for (let i = 0; i <= divideNum; i++) {
      let y = i * priceStep * priceRatio;
      // 绘制Y轴价格刻度分割线
      ctx.beginPath();
      ctx.moveTo(scaleWidth, -y);
      ctx.lineTo(canvasWidth, -y);
      ctx.strokeStyle = '#8d9191';
      ctx.stroke();
      ctx.closePath() //关闭画线
    }
    ctx.restore() //重置原点(0,0)

    // 绘制蜡烛图
    ctx.save();   //保存初始状态，原点(0,0)
    ctx.translate(scaleWidth, canvasHeight - kChartToBottom) // 重新映射画布上的 (0,0) 位置为新的位置(x,y)
    let showDateIndex = 0;
    dayPriceArray.forEach(([open, close, low, high], i) => {
      let x = i * itemWidth * 2 + itemWidth / 2
      // 绘制影线
      ctx.beginPath();
      ctx.moveTo(x, (high - minPrice) * priceRatio * -1);
      ctx.lineTo(x, (low - minPrice) * priceRatio * -1);
      if (close >= open) {
        ctx.strokeStyle = upColor
      } else {
        ctx.strokeStyle = downColor
      }
      ctx.stroke();
      // 绘制实体
      let height = Math.abs(close - open) * priceRatio;
      if (height === 0) {
        height = 1
      }
      let closeOpenMax = Math.max(close, open)
      if (close >= open) {
        ctx.fillStyle = upColor
      } else {
        ctx.fillStyle = downColor
      }
      ctx.fillRect(i * itemWidth * 2, (closeOpenMax - minPrice) * priceRatio * -1, itemWidth, height);

      // 绘制日期
      ctx.font = '14px Arial';
      ctx.textAlign = 'center';
      if (i === showDateIndex) {
        ctx.fillText(dateData[i + showStartIndex], x, 15);
        showDateIndex += Math.floor(dayPriceArray.length / showDateNum);
      }
    });
    ctx.restore() //重置原点(0,0)

    /**
     * 绘制均线
     * @param maData 均线数据格式: [{color:'',ma:[a,b...]}]
     */
    function drawAverageLine(maData) {
      maData.forEach((n) => {
        let ma = n.ma.slice(showStartIndex, showEndIndex);
        let firstNonZeroIndex = ma.indexOf(ma.find(num => num))

        if (firstNonZeroIndex > -1) {
          //绘制均线
          ctx.save();   //保存初始状态，原点(0,0)
          ctx.translate(scaleWidth, canvasHeight - kChartToBottom) // 重新映射画布上的 (0,0) 位置为新的位置(x,y)
          ctx.beginPath();
          ctx.strokeStyle = n.color;
          ctx.moveTo(firstNonZeroIndex * itemWidth * 2 + itemWidth / 2, (ma[firstNonZeroIndex] - minPrice) * priceRatio * -1);
          n.ma.slice(showStartIndex, showEndIndex).forEach((p, i) => {
            // 绘制均线
            if (p > 0) {
              ctx.lineTo(i * itemWidth * 2 + itemWidth / 2, (p - minPrice) * priceRatio * -1);
            }
          });
          ctx.stroke();
          ctx.restore() //重置原点(0,0)
        }
      });
    }

    drawAverageLine(maPriceArray)
  }

  /**
   * 画成交量
   */
  function drawVolume(volumeData) {
    volumeCtx.clearRect(0, 0, volumeWidth, volumeHeight)

    let max = Math.max(...volumeData.flat(1));
    let heightRatio = (volumeHeight - indexChartToTop - indexChartToBottom) / max; // 计算坐标比例,定义单位成交量所占的高度
    console.log('最大成交量,比例', max, heightRatio)

    // 绘制蜡烛图
    volumeCtx.save();   //保存初始状态，原点(0,0)
    volumeCtx.translate(scaleWidth, volumeHeight - indexChartToBottom) // 重新映射画布上的 (0,0) 位置为新的位置(x,y)
    volumeData.forEach((v, i) => {
      // 绘制实体
      let height = v * heightRatio;
      height = height < 1 ? 1 : height
      if (kLineData[i + showStartIndex][1] >= kLineData[i + showStartIndex][0]) {
        volumeCtx.fillStyle = upColor
      } else {
        volumeCtx.fillStyle = downColor
      }
      volumeCtx.fillRect(i * itemWidth * 2, 0, itemWidth, -height);
    });
    volumeCtx.restore() //重置原点(0,0)
  }

  function drawKDJ(k, d, j) {
    kdjCtx.clearRect(0, 0, kdjWidth, kdjHeight);
    // 计算最大和最小值
    let maxVal = Math.max(...k.flat(1), ...d.flat(1), ...j.flat(1));
    let minVal = Math.min(...k.flat(1), ...d.flat(1), ...j.flat(1));

    // 计算绘图区域尺寸
    let paddingLeft = scaleWidth + itemWidth / 2;
    let paddingTop = 5;
    let paddingBottom = 5;
    let plotHeight = kdjHeight - paddingBottom - paddingTop;
    let scaleY = plotHeight / (maxVal - minVal);
    let step = itemWidth * 2;

    // 绘制超卖区域
    kdjCtx.beginPath();
    kdjCtx.strokeStyle = '#ce3a3a';
    kdjCtx.font = '12px Arial';
    kdjCtx.textAlign = 'center';
    kdjCtx.lineWidth = 1;
    k.forEach((v, i) => {
      if (k[i] < 20 && d[i] < 20 && j[i] < 0) {
        if (i !== k.length - 1) {
          //超卖
          let x = paddingLeft + step * i;
          if (k[i + 1] < 20 && d[i + 1] < 20 && j[i + 1] < 0) {
            kdjCtx.fillStyle = '#FF00004C'; // 超买区域填充色
            kdjCtx.fillRect(x, paddingTop, step, kdjHeight - paddingBottom - paddingTop);
          } else {
            kdjCtx.moveTo(x, paddingTop);
            kdjCtx.lineTo(x, kdjHeight - paddingBottom);
            kdjCtx.fillText(dateData[i + showStartIndex], x, kdjHeight - paddingBottom + 15);
          }
        }
      }
    });
    kdjCtx.stroke();

    // 绘制K线
    kdjCtx.beginPath();
    kdjCtx.strokeStyle = '#000000';
    kdjCtx.lineWidth = 1;
    k.forEach((n, i) => {
      let y = kdjHeight - paddingBottom - ((n - minVal) * scaleY);
      let x = paddingLeft + step * i;
      kdjCtx.lineTo(x, y);
    })
    kdjCtx.stroke();

    // 绘制D曲线
    kdjCtx.beginPath();
    kdjCtx.strokeStyle = '#fc6900';
    kdjCtx.lineWidth = 1;
    d.forEach((n, i) => {
      let y = kdjHeight - paddingBottom - ((n - minVal) * scaleY);
      let x = paddingLeft + step * i;
      kdjCtx.lineTo(x, y);
    })
    kdjCtx.stroke();

    // 绘制J柱状图
    kdjCtx.beginPath();
    kdjCtx.strokeStyle = '#ff0011';
    kdjCtx.lineWidth = 1;
    j.forEach((n, i) => {
      let y = kdjHeight - paddingBottom - ((n - minVal) * scaleY);
      let x = paddingLeft + step * i;
      kdjCtx.lineTo(x, y);
    })
    kdjCtx.stroke();
  }

  function drawMACD(dif, dea, macd) {
    macdCtx.clearRect(0, 0, macdWidth, macdHeight);
    // 计算最大和最小值
    let maxVal = Math.max(...dif.flat(1), ...dea.flat(1), ...macd.flat(1));
    let minVal = Math.min(...dif.flat(1), ...dea.flat(1), ...macd.flat(1));

    // 计算绘图区域尺寸
    let paddingLeft = scaleWidth + itemWidth / 2;
    let paddingRight = 0;
    let paddingTop = 5;
    let paddingBottom = 15;
    let plotWidth = macdWidth - paddingLeft - paddingRight;
    let plotHeight = macdHeight - paddingBottom - paddingTop;
    let step = itemWidth * 2;
    let scaleY = plotHeight / (maxVal - minVal);

    // 绘制X轴日期和网格线
    let showDateNum = 3;        //显示日期的个数
    let showDateIndex = 0;      //显示日期的下标
    macdCtx.beginPath();
    macdCtx.strokeStyle = '#807D7D16';
    macdCtx.fillStyle = '#333';
    macdCtx.font = '12px Arial';
    macdCtx.textAlign = 'center';
    macdCtx.lineWidth = 1;
    dif.forEach((v, i) => {
      let x = paddingLeft + step * i;
      macdCtx.moveTo(x, paddingTop);
      macdCtx.lineTo(x, macdHeight - paddingBottom);
      if (i === showDateIndex) {
        macdCtx.fillText(dateData[i + showStartIndex], x, macdHeight - paddingBottom + 15);
        showDateIndex += Math.floor(dif.length / showDateNum);
      }
    });
    macdCtx.stroke();

    // 绘制Y纵轴刻度线和标签
    let divideNum = 6;
    console.log('等分：', divideNum)
    console.log('macd最大值,最小值,比例', maxVal, minVal, scaleY)
    console.log('canvas宽高', macdWidth, macdHeight)
    console.log('plot图宽高', plotWidth, plotHeight)
    let scaleStep = parseFloat(((maxVal - minVal) / divideNum).toFixed(2));
    console.log('scaleStep', scaleStep)
    macdCtx.beginPath();
    macdCtx.strokeStyle = '#807D7D16';
    macdCtx.fillStyle = '#333';
    macdCtx.lineWidth = 1;
    console.log('最大值对应的canvas间隔像素:', (maxVal - minVal) * scaleY)
    console.log('最小值对应的canvas间隔像素:', (minVal - minVal) * scaleY)
    console.log('scaleStep对应的canvas像素:', scaleStep * scaleY)
    for (let j = 0; j <= divideNum; j++) {
      let y = paddingTop + (scaleStep * scaleY * j);
      macdCtx.moveTo(paddingLeft, y);
      macdCtx.lineTo(macdWidth - paddingRight, y);
      macdCtx.textAlign = 'right';
      macdCtx.fillText((maxVal - j * (maxVal - minVal) / divideNum).toFixed(2), paddingLeft, y);
    }
    macdCtx.stroke();

    // 绘制macd柱状图
    macd.forEach((n, i) => {
      let x = paddingLeft + step * i;
      let y = macdHeight - paddingBottom - ((n - minVal) * scaleY);

      if (n >= 0) {
        macdCtx.fillStyle = upColor;
        macdCtx.fillRect(x - 2, y, 4, n * scaleY);
      } else {
        macdCtx.fillStyle = downColor;
        macdCtx.fillRect(x - 2, y, 4, n * scaleY);
      }
    })

    // 绘制dif曲线
    macdCtx.beginPath();
    macdCtx.strokeStyle = '#000000';
    macdCtx.lineWidth = 1;

    dif.forEach((n, i) => {
      let difY = macdHeight - paddingBottom - ((n - minVal) * scaleY);
      let x = paddingLeft + step * i;
      macdCtx.lineTo(x, difY);
    })
    macdCtx.stroke();

    // 绘制dea曲线
    macdCtx.beginPath();
    macdCtx.strokeStyle = '#ff0011';
    macdCtx.lineWidth = 1;
    dea.forEach((n, i) => {
      let deaY = macdHeight - paddingBottom - ((n - minVal) * scaleY);
      let x = paddingLeft + step * i;
      macdCtx.lineTo(x, deaY);
    })
    macdCtx.stroke();

  }

  function drawBOLL(upper, mid, lower, candleData) {
    bollCtx.clearRect(0, 0, bollWidth, bollHeight);
    // 计算最大和最小值
    let maxVal = Math.max(...upper.flat(1), ...lower.flat(1), ...mid.flat(1));
    let minVal = Math.min(...(upper.filter(n => n > 0)), ...(lower.filter(n => n > 0)), ...(mid.filter(n => n > 0)));

    // 计算绘图区域尺寸
    let paddingLeft = scaleWidth + itemWidth / 2;
    let paddingTop = 5;
    let paddingBottom = 5;
    let plotHeight = bollHeight - paddingBottom - paddingTop;
    let scaleY = plotHeight / (maxVal - minVal);
    let step = itemWidth * 2;

    // 绘制upper曲线
    bollCtx.beginPath();
    bollCtx.strokeStyle = upColor;
    bollCtx.lineWidth = 1;
    upper.forEach((n, i) => {
      let x = paddingLeft + step * i;
      let y = bollHeight - paddingBottom - ((n - minVal) * scaleY);
      bollCtx.lineTo(x, y);
    })
    bollCtx.stroke();

    // 绘制mid曲线
    bollCtx.beginPath();
    bollCtx.strokeStyle = '#2366de';
    bollCtx.lineWidth = 1;
    mid.forEach((n, i) => {
      let x = paddingLeft + step * i;
      let y = bollHeight - paddingBottom - ((n - minVal) * scaleY);
      bollCtx.lineTo(x, y);
    })
    bollCtx.stroke();

    // 绘制lower曲线
    bollCtx.beginPath();
    bollCtx.strokeStyle = downColor;
    bollCtx.lineWidth = 1;
    lower.forEach((n, i) => {
      let x = paddingLeft + step * i;
      let y = bollHeight - paddingBottom - ((n - minVal) * scaleY);
      bollCtx.lineTo(x, y);
    })
    bollCtx.stroke();

    // 绘制蜡烛图
    candleData.forEach(([open, close, low, high], i) => {
      let x = paddingLeft + step * i;
      // 绘制影线
      bollCtx.beginPath();
      bollCtx.moveTo(x, bollHeight - paddingBottom - ((high - minVal) * scaleY));
      bollCtx.lineTo(x, bollHeight - paddingBottom - ((low - minVal) * scaleY));
      if (close >= open) {
        bollCtx.strokeStyle = upColor
      } else {
        bollCtx.strokeStyle = downColor
      }
      bollCtx.stroke();
      // 绘制实体
      let height = Math.abs(close - open) * scaleY;
      if (height === 0) {
        height = 1
      }
      let closeOpenMax = Math.max(close, open)
      if (close >= open) {
        bollCtx.fillStyle = upColor
      } else {
        bollCtx.fillStyle = downColor
      }
      bollCtx.fillRect(x - 1, bollHeight - paddingBottom - ((closeOpenMax - minVal) * scaleY), 2, height);
    });
  }

  /**
   * 渲染所有的图形
   */
  function renderAll() {
    //绘制K线图
    drawKLine(kLineData.slice(showStartIndex, showEndIndex), maPriceArray)
    //绘制成交量
    drawVolume(volumeData.slice(showStartIndex, showEndIndex))
    //绘制KDJ
    drawKDJ(kdjData.k.slice(showStartIndex, showEndIndex), kdjData.d.slice(showStartIndex, showEndIndex), kdjData.j.slice(showStartIndex, showEndIndex))
    //绘制MACD
    drawMACD(macdData.dif.slice(showStartIndex, showEndIndex), macdData.dea.slice(showStartIndex, showEndIndex), macdData.macd.slice(showStartIndex, showEndIndex))
    //绘制BOLL
    drawBOLL(bollData.upper.slice(showStartIndex, showEndIndex), bollData.mid.slice(showStartIndex, showEndIndex), bollData.lower.slice(showStartIndex, showEndIndex), kLineData.slice(showStartIndex, showEndIndex))
  }
};


/*---------------------------------工具类---------------------------------*/

/**
 * 计算均线数据
 * @param dayCount 均线天数
 * @param dayPrice 每天的价格数据[[open,close,low,high],...]
 */
function calculateMA(dayCount, dayPrice) {
  let movingAverages = [];
  let sum = 0;
  if (dayCount > dayPrice.length) {
    return Array(dayPrice.length).fill(0)
  } else if (dayCount === dayPrice.length) {
    sum = dayPrice.reduce((acc, cur) => acc + cur[1], 0);
    movingAverages = Array(dayPrice.length - 1).fill(0)
    movingAverages.push(sum / dayCount);
  } else {
    sum = dayPrice.slice(0, dayCount).reduce((acc, cur) => acc + cur[1], 0);
    movingAverages = Array(dayCount - 1).fill(0)
    movingAverages.push(sum / dayCount);
    for (let i = dayCount; i < dayPrice.length; i++) {
      sum = sum + dayPrice[i][1] - dayPrice[i - dayCount][1];
      movingAverages.push(sum / dayCount);
    }
  }
  return movingAverages;
}

/**
 * 计算KDJ,计算方法如下：
 *
 * 1. 计算RSV值: RSV = （收盘价 - 最低价）/（最高价 - 最低价）×100%,最低价和最高价是过去9个交易日（包括当天）的最低价和最高价，收盘价是当日的收盘价
 * 2. 计算K值（K线）: K值 = 2/3 × 前一日K值 + 1/3 × 当日RSV
 * 3. 计算D值（D线）: D值 = 2/3 × 前一日D值 + 1/3 × 当日K值
 * 4. 计算J值（J线）: J值 = 3 × 当日K值 - 2 × 当日D值
 *
 * @param data
 * @returns {{d: *[], j: *[], k: *[]}}
 */
function calculateKDJ(data) {
  let kVal = [];
  let dVal = [];
  let jVal = [];
  for (let i = 0; i < data.length; i++) {
    if (i < 8) {
      kVal.push(50);
      dVal.push(50);
      jVal.push(0);
      continue
    }

    let c = data[i][1]; // 当日收盘价
    let l9 = Math.min(...data.slice(i - 8, i + 1).flat(1)); // 9日最低价
    let h9 = Math.max(...data.slice(i - 8, i + 1).flat(1)); // 9日最高价
    let rsv = (c - l9) / (h9 - l9) * 100;   // 当日RSV值
    let k = parseFloat((kVal[i - 1] * 2 / 3 + rsv / 3).toFixed(2));  // 当日K值（快速线）
    let d = parseFloat((dVal[i - 1] * 2 / 3 + k / 3).toFixed(2));    // 当日D值（慢速线）
    let j = parseFloat((3 * k - 2 * d).toFixed(2));                  // 当日J值（买卖强弱指标）
    kVal.push(k);
    dVal.push(d);
    jVal.push(j);
  }
  return {k: kVal, d: dVal, j: jVal};
}

/**
 * 计算MACD
 * @param closePrices 收盘价数组
 * @returns {{dif: *[], dea: *[], macd: *[]}}
 */
function calculateMACD(closePrices) {
  let difArray = [];
  let deaArray = [];
  let macdArray = [];

  let ema12 = getEMA(closePrices, 12);
  let ema26 = getEMA(closePrices, 26);
  for (let i = 0; i < closePrices.length; i++) {
    difArray[i] = parseFloat((ema12[i] - ema26[i]).toFixed(4));
  }
  deaArray[0] = difArray[0];
  for (let i = 1; i < closePrices.length; i++) {
    deaArray[i] = parseFloat(((deaArray[i - 1] * 8 / 10) + (difArray[i] * 2 / 10)).toFixed(4));
  }
  for (let i = 0; i < closePrices.length; i++) {
    macdArray[i] = parseFloat((2 * (difArray[i] - deaArray[i])).toFixed(4));
  }

  return {
    dif: difArray,
    dea: deaArray,
    macd: macdArray
  };
}

/**
 * 计算BOLL,计算公式如下:
 * 中轨线 = N日的移动平均线
 * 上轨线 = 中轨线 + 两倍的标准差
 * 下轨线 = 中轨线 - 两倍的标准差
 * @returns {{upper: *[], lower: *[], mid: *[]}}
 */
function calculateBOLL(kLineData) {
  let upperArray = [];
  let lowerArray = [];
  let closePrices = kLineData.map(n => n[1])
  let maArray = calculateMA(20, kLineData);
  let standardDeviationArray = calculateStandardDeviation(closePrices, 20)
  closePrices.forEach((n, i) => {
    upperArray.push(maArray[i] + 2 * standardDeviationArray[i]);
    lowerArray.push(maArray[i] - 2 * standardDeviationArray[i]);
  })
  return {
    upper: upperArray,
    lower: lowerArray,
    mid: maArray
  };
}

function calculateStandardDeviation(data, n) {
  const standardDeviation = [];
  for (let i = 0; i < data.length; i++) {
    if (i < 20) {
      standardDeviation.push(0);
      continue
    }
    const subset = data.slice(i - n + 1, i + 1);
    const average = subset.reduce((total, value) => total + value, 0) / n;
    const differences = subset.map(value => value - average);
    const squaredDifferences = differences.map(value => value ** 2);
    const sumSquaredDifferences = squaredDifferences.reduce((total, value) => total + value, 0);
    const variance = sumSquaredDifferences / n;
    const sd = Math.sqrt(variance);
    standardDeviation.push(sd);
  }

  return standardDeviation;
}

function getEMA(prices, n) {
  let emaArray = [];
  emaArray[0] = prices[0];
  for (let i = 1; i < prices.length; i++) {
    emaArray[i] = (2 * prices[i] + (n - 1) * emaArray[i - 1]) / (n + 1);
  }
  return emaArray;
}

/**
 * 计算第一次展示烛图的比例个数
 * @param length
 * @returns {number}
 */
function calculateShowCandleNum(length) {
  let n
  if (length < 100) {
    n = length
  } else if (length < 200) {
    n = 100
  } else if (length < 500) {
    n = 150
  } else if (length < 3000) {
    n = 200
  } else {
    n = Math.floor(length / 10)
  }
  return n;
}
